package umg.os.kernel.scheduler;

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.nio.file.attribute.BasicFileAttributes;

import umg.os.process.ProcessControlBlock;
import umg.os.process.ProcessControlBlock.ProcessStatus;
import umg.os.timer.Timer;
import umg.os.timer.Timer.TimerListener;


public class ReadyList implements TimerListener {

	private static final ReadyList singletonInstance = new ReadyList();
	
	List<ProcessControlBlock> processWaiting;
	List<ProcessControlBlock> processCompleated;

	private ReadyList() {
		processCompleated = new ArrayList<ProcessControlBlock>();
	}
	
	static ReadyList getInstance(){
		return singletonInstance;
	}
	
	public static void loadProgram(Path procFilesDir) {
		singletonInstance.processWaiting = new ArrayList<ProcessControlBlock>();
		try {
			Files.walkFileTree(procFilesDir, new SimpleFileVisitor<Path>() {
				@Override
				public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
					return FileVisitResult.CONTINUE;
				}

				@Override
				public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
					if (file.toString().endsWith(".proc")) {
						//singletonInstance.processWaiting.add(ProcessControlBlock.instantiateFromProcFile(file));
						ProcessControlBlock pcb = ProcessControlBlock.instantiateFromProcFile(file);
						System.out.println(pcb);
						singletonInstance.processWaiting.add(pcb);
					}
					return FileVisitResult.CONTINUE;
				}
			});
		} catch (IOException e) {
			System.err.println("It was impossible to read all the files of the parent directory");
		}
		Collections.sort(singletonInstance.processWaiting, Scheduler.getInstance().getSorter());
	}
	
	/**
	 * Removes from the list the next process in the queue and retrieve it to its requester.
	 * 
	 * @return 
	 * returns the immediately next process in the queue of {@link #processWaiting}.
	 */
	ProcessControlBlock getNextWaitingProcess(){
		if(processWaiting == null){
			throw new ProgramsNotLoadedException("There is not a program loaded in memory");
		}
		if(processWaiting.size()<=0){
			System.out.println("\nNO MORE PROCESS");
			System.exit(0);
			//throw new NullPointerException("No more process");
		}
		if (Timer.getCurrentCycle() >= processWaiting.get(0).arrivalTime) {
			ProcessControlBlock processToSend = processWaiting.get(0);
			processWaiting.remove(0);
			return processToSend;
		} else {
			return null;
		}
	}
	
	/**
	 * Schedule <b>pcb</b> at the end of the queue {@link #processWaiting}.
	 * 
	 * @param pcb
	 * The process to be scheduled.
	 */
	void scheduleAtLast(ProcessControlBlock pcb){
		if(processWaiting == null){
			throw new ProgramsNotLoadedException("There is not a program loaded in memory");
		}
		if(pcb == null){
			throw new NullPointerException("The pcb cannot be null");
		}
		if (!pcb.getStatus().equals(ProcessStatus.TERMINATED)) {
			processCompleated.add(pcb);
		}
		processWaiting.add(pcb);
	}
	
	@Override
	public void nextTimeUnit() {
		for (ProcessControlBlock pcb : processWaiting) {
			pcb.increaseWaitingTime();
		}
	}
	
	@SuppressWarnings("serial")
	private class ProgramsNotLoadedException extends RuntimeException {
		
		private ProgramsNotLoadedException(String message) {
			super(message);
		}
		
	}
	
}
